<template>
    <div class="audio-viewable-container">
        <canvas class="audio-viewable-content" :width=canvasSize.width :height=canvasSize.height></canvas>
        <audio class="audio-viewable-audio" controls>
            <source src="/audio/Dearest.mp3" type="audio/mpeg">
        </audio>
        <div class="audio-viewable-bts">
            <button class="audio-viewable-bt" @click="clickShockwave" v-show="shape !== shockwave">时域震荡波</button>
            <button class="audio-viewable-bt" @click="clickRect" v-show="shape !== rect">频域柱状图</button>
        </div>
    </div>
</template>

<script>
export default {
    name: "ViewableAudio",
    components: {},
    data() {
        return {
            canvasCtx: null,
            canvasSize: {
                width: 0,
                height: 0,
            },
            analyser: null,
            fftSize: 512,
            dataArray: [],
            stop: false,
            shape: "Shockwave",
            shockwave: "Shockwave",
            rect: "Rectangular",
        }
    },
    computed: {},
    methods: {
        clickShockwave() {
            this.shape = this.shockwave;
        },
        clickRect() {
            this.shape = this.rect;
        },
        draw() {
            if (this.stop) {
                return;
            }

            switch (this.shape) {
                case this.shockwave:
                    this.drawShockwave();
                    break;
                case this.rect:
                    this.drawRect();
                    break;
                default:
                    break;
            }

            requestAnimationFrame(this.draw.bind(this));
        },
        //频域柱状图
        drawRect() {
            this.analyser.getByteFrequencyData(this.dataArray);

            this.canvasCtx.fillStyle = "rgb(200, 200, 200)";
            this.canvasCtx.fillRect(0, 0, this.canvasSize.width, this.canvasSize.height);

            this.canvasCtx.fillStyle = "#78C5F7";

            const bufferLength = this.analyser.frequencyBinCount;
            const barWidth = this.canvasSize.width / 2 / bufferLength;
            let x1 = 0;
            let x2 = 0;

            for (let i = 0; i < bufferLength; i++) {
                const v = this.dataArray[i] / 255;
                const barHeight = v * this.canvasSize.height;
                const y = this.canvasSize.height - barHeight;

                x1 = this.canvasSize.width / 2 - (i + 1) * barWidth;
                x2 = this.canvasSize.width / 2 + i * barWidth;
                this.canvasCtx.fillRect(x1, y, barWidth, barHeight);
                this.canvasCtx.fillRect(x2, y, barWidth, barHeight);
            }
        },

        //时域震荡波
        drawShockwave() {
            this.analyser.getByteTimeDomainData(this.dataArray);

            this.canvasCtx.fillStyle = "rgb(200, 200, 200)";
            this.canvasCtx.fillRect(0, 0, this.canvasSize.width, this.canvasSize.height);

            this.canvasCtx.lineWidth = 2;
            this.canvasCtx.strokeStyle = "rgb(0, 0, 0)";

            this.canvasCtx.beginPath();

            const bufferLength = this.analyser.frequencyBinCount;
            const sliceWidth = (this.canvasSize.width * 1.0) / bufferLength;
            let x = 0;

            for (let i = 0; i < bufferLength; i++) {
                const v = this.dataArray[i] / 128.0;
                const y = (v * this.canvasSize.height) / 2;

                if (i === 0) {
                    this.canvasCtx.moveTo(x, y);
                } else {
                    this.canvasCtx.lineTo(x, y);
                }

                x += sliceWidth;
            }

            this.canvasCtx.lineTo(this.canvasSize.width, this.canvasSize.height / 2);
            this.canvasCtx.stroke();
        },
    },
    mounted() {
        const container = this.$el;
        const canvas = this.$el.querySelector(".audio-viewable-content");
        //初始化画布大小
        this.canvasSize.width = container.offsetWidth;
        this.canvasSize.height = container.offsetHeight / 2;
        this.canvasCtx = canvas.getContext("2d");

        const AudioContext = window.AudioContext || window.webkitAudioContext;

        const audioCtx = new AudioContext();

        const audioElement = this.$el.querySelector(".audio-viewable-audio");
        const source = audioCtx.createMediaElementSource(audioElement);

        this.analyser = audioCtx.createAnalyser();
        this.analyser.fftSize = this.fftSize;
        this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);

        source.connect(this.analyser);
        this.analyser.connect(audioCtx.destination);

        this.draw();
    },
    beforeUnmount() {
        this.stop = true;
        if (this.analyser) {
            this.analyser.disconnect();
        }
    }
}
</script>

<style scoped>
.audio-viewable-container {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

.audio-viewable-content {
    position: absolute;
    top: 0;
}

.audio-viewable-audio {
    position: absolute;
    top: 55%;
}

.audio-viewable-bts {
    position: absolute;
    width: 60%;
    height: 40%;
    top: 60%;
    left: 20%;
    display: flex;
    justify-content: center;
    align-items: center;
}

.audio-viewable-bt {
    margin: 10px;
}
</style>